﻿using System;
using System.Collections.Concurrent;

namespace DBUtil
{
    /// <summary>
    /// 支持多个数据库类型
    /// </summary>
    public static class DBFactory
    {
        private static readonly ConcurrentDictionary<Type, IDBFactory<DBAccess>> dic_factorys = new();

        private static readonly IDBFactory<DBAccess> sqlserverFactory = null;
        private static readonly IDBFactory<DBAccess> mysqlFactory = null;
        private static readonly IDBFactory<DBAccess> oracleFactory = null;
        private static readonly IDBFactory<DBAccess> postgresqlFactory = null;
        private static readonly IDBFactory<DBAccess> sqliteFactory = null;

        #region 自动加载数据库驱动
        static DBFactory()
        {
            try
            {
                var type = Type.GetType("DBUtil.Provider.SqlServer.SqlServerDBFactory, DBUtil.Provider.SqlServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
                if (type != null)
                {
                    sqlserverFactory = Activator.CreateInstance(type) as IDBFactory<DBAccess>;
                    AddDBSupport(sqlserverFactory);
                }
            }
            catch { }
            try
            {
                var type = Type.GetType("DBUtil.Provider.MySql.MySqlDBFactory, DBUtil.Provider.MySql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
                if (type != null)
                {
                    mysqlFactory = Activator.CreateInstance(type) as IDBFactory<DBAccess>;
                    AddDBSupport(mysqlFactory);
                }
            }
            catch { }
            try
            {
                var type = Type.GetType("DBUtil.Provider.Oracle.OracleDBFactory, DBUtil.Provider.Oracle, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
                if (type != null)
                {
                    oracleFactory = Activator.CreateInstance(type) as IDBFactory<DBAccess>;
                    AddDBSupport(oracleFactory);
                }
            }
            catch { }
            try
            {
                var type = Type.GetType("DBUtil.Provider.PostgreSql.PostgreSqlDBFactory, DBUtil.Provider.PostgreSql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
                if (type != null)
                {
                    postgresqlFactory = Activator.CreateInstance(type) as IDBFactory<DBAccess>;
                    AddDBSupport(postgresqlFactory);
                }
            }
            catch { }
            try
            {
                var type = Type.GetType("DBUtil.Provider.SQLite.SQLiteDBFactory, DBUtil.Provider.SQLite, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
                if (type != null)
                {
                    sqliteFactory = Activator.CreateInstance(type) as IDBFactory<DBAccess>;
                    AddDBSupport(sqliteFactory);
                }
            }
            catch { }
        }
        #endregion

        /// <summary>
        /// 添加数据库类型支持
        /// </summary>
        /// <typeparam name="T">特定数据库类型的工厂,比如:SqlServerDBFactory</typeparam>
        public static void AddDBSupport<T>() where T : IDBFactory<DBAccess>, new()
        {
            var type = typeof(T);
            var instance = new T();
            dic_factorys.GetOrAdd(type, instance);
        }

        /// <summary>
        /// 添加数据库类型支持
        /// </summary>
        public static void AddDBSupport(IDBFactory<DBAccess> factory)
        {
            if (factory == null) return;
            dic_factorys.GetOrAdd(factory.GetType(), factory);
        }

        /// <summary>
        /// 创建 DBAccess 对象, 如:
        /// <list type="bullet">
        /// <item>DBFactory.CreateDB("SQLSERVER","Data Source=.;Initial Catalog=test;User ID=sa;Password=123456;")</item>
        /// <item>DBFactory.CreateDB("ORACLE","Data Source=ORCTest;Password=123456;User ID=sys;DBA Privilege=SYSDBA;")</item>
        /// <item>DBFactory.CreateDB("MYSQL","Server=127.0.0.1;Database=test;Uid=root;Pwd=123456;AllowLoadLocalInfile=true;SslMode=none;AllowPublicKeyRetrieval=True;Charset=utf8mb4;")</item>
        /// <item>DBFactory.CreateDB("POSTGRESQL","Server=localhost;Port=5432;UserId=postgres;Password=123456;Database=test;")</item>
        /// <item>DBFactory.CreateDB("SQLITE","Data Source=f:\test.db;")</item>
        /// </list>
        /// </summary>
        public static DBAccess CreateDB(string dbType, string dbConn, DBSetting Settings = null)
        {
            Settings ??= DBSetting.NewInstance();
            DBAccess db = null;
            foreach (var factory in dic_factorys.Values)
            {
                db = factory.CreateDB(dbType, dbConn, Settings);
                if (db != null) return db;
            }
            if (db == null) throw new Exception($"未找到支持的数据库类型:{dbType},请下载对应的nuget包,并调用[DBFactory.AddDBSupport<{dbType}DBFactory>()]");
            return db;
        }

        /// <summary>
        /// 创建 DBAccess 对象, 如:
        /// <list type="bullet">
        /// <item>DBFactory.CreateDB(DBUtil.DBType.SQLSERVER,"Data Source=.;Initial Catalog=test;User ID=sa;Password=123456;")</item>
        /// <item>DBFactory.CreateDB(DBUtil.DBType.ORACLE,"Data Source=ORCTest;Password=123456;User ID=sys;DBA Privilege=SYSDBA;")</item>
        /// <item>DBFactory.CreateDB(DBUtil.DBType.MYSQL,"Server=127.0.0.1;Database=test;Uid=root;Pwd=123456;AllowLoadLocalInfile=true;SslMode=none;AllowPublicKeyRetrieval=True;Charset=utf8mb4;")</item>
        /// <item>DBFactory.CreateDB(DBUtil.DBType.POSTGRESQL,"Server=localhost;Port=5432;UserId=postgres;Password=123456;Database=test;")</item>
        /// <item>DBFactory.CreateDB(DBUtil.DBType.SQLITE,"Data Source=f:\test.db;")</item>
        /// </list>
        /// </summary>
        public static DBAccess CreateDB(DBType dbType, string dbConn, DBSetting Settings = null)
        {
            Settings ??= DBSetting.NewInstance();
            DBAccess db = null;
            switch (dbType)
            {
                case DBType.SQLSERVER:
                    {
                        db = sqlserverFactory.CreateDB(dbType.ToString(), dbConn, Settings);
                        break;
                    }
                case DBType.MYSQL:
                    {
                        db = mysqlFactory.CreateDB(dbType.ToString(), dbConn, Settings);
                        break;
                    }
                case DBType.ORACLE:
                    {
                        db = oracleFactory.CreateDB(dbType.ToString(), dbConn, Settings);
                        break;
                    }
                case DBType.POSTGRESQL:
                    {
                        db = postgresqlFactory.CreateDB(dbType.ToString(), dbConn, Settings);
                        break;
                    }
                case DBType.SQLITE:
                    {
                        db = sqliteFactory.CreateDB(dbType.ToString(), dbConn, Settings);
                        break;
                    }
            }
            return db;
        }
    }
}
